#include "quirrel_editor_extension.hpp"
#include <iterator>

#include <vector>
#include <sqrat.h>
#include <fmt/printf.h>
#include <quirrel/squirrel/sqvm.h>

#include "data/editor_data.hpp"
#include "quirrel_base.hpp"
#include "scripting/scripting_api.h"
#include "scripting/scripting_module.h"
struct QuirrelEditorExtensionPrivate {
    std::vector<uint8_t>       byte_code;
};

QuirrelEditorExtension::QuirrelEditorExtension (IEditorScript::EditorTarget type, std::string title, std::string script_path):
    IEditorScript(type, std::move(title), std::move(script_path), g_current_module) ,d{new QuirrelEditorExtensionPrivate}
{
}

QuirrelEditorExtension::~QuirrelEditorExtension ()
{
    delete d;
}

void QuirrelEditorExtension::execute (const void *node)
{
    // return false;
    struct closure_read_data {
        uint8_t* const data;
        SQInteger      length;
        SQInteger      read;
    };

    closure_read_data data{
        .data = d->byte_code.data(),
        .length = static_cast<SQInteger> (d->byte_code.size ()),
        .read =0
    };

    VMHelper &helper = VMHelper::GetVMInstance();

    auto result = sq_readclosure(helper, +[](SQUserPointer p1, SQUserPointer p2, SQInteger p3)->SQInteger {
        const auto data = static_cast<closure_read_data *> (p1);
        memcpy(p2, data->data + data->read, p3);
        data->read += p3;
        return p3;
    }, &data);
    SQ_CHECK_RV(result, "Closure deserialize error", helper);

    //!! run the closure first
    sq_pushroottable(helper);
    result = sq_call(helper, 1, true, true);
    SQ_CHECK_RV(result, "Closure failed to call", helper);
    
    Sqrat::RootTable table{ helper };
    auto main = table.GetFunction("main");
    if (!main.IsNull ())
    {
        switch (apply_target_)
        {
        case IEditorScript::eItem:  main.Execute(static_cast<const ItemNode*>(node));break;
        case IEditorScript::eDay:   main.Execute(static_cast<const DayLevelNode*>(node));break;
        case IEditorScript::eMonth: main.Execute(static_cast<const MonthLevelNode*>(node));break;
        case IEditorScript::eYear:  main.Execute(static_cast<const YearLevelNode*>(node));break;
        default: main.Execute ("N/A");
        }
        return;
    }

    SQ_CHECK_RV(SQ_ERROR, "function main not found!!", helper);
}

IScript* QuirrelEditorExtension::create (IEditorScript::EditorTarget target, std::string title, std::string path, const std::string_view &code)
{
    auto extension = new QuirrelEditorExtension (target, std::move(title), std::move(path));
    if (!extension->compile (code))
    {
        delete extension;
        extension = nullptr;
    }
    return extension;
}

bool QuirrelEditorExtension::compile (const std::string_view &code) const
{
    VMHelper &helper = VMHelper::GetVMInstance();

    auto result = sq_compilebuffer (helper, code.data (), code.length (), "main", true);
    SQ_CHECK_R(result, "Code Compile Error", helper, false);
    result = sq_writeclosure (helper, +[](SQUserPointer p1, SQUserPointer p2, SQInteger p3)-> SQInteger {
        const auto data      = static_cast<uint8_t*>(p2);
        const auto byte_code = static_cast<std::vector<unsigned char> *>(p1);
        std::copy_n(data, p3, std::back_inserter (*byte_code));
        return p3;
    }, &d->byte_code);
    SQ_CHECK_R(result, "Closure serialize error", helper, false);
    return true;
}
